package com.ztrue.framework.core.domain;

import com.ztrue.framework.context.ApplicationContextProvider;
import com.ztrue.framework.context.IAggregateRootInternalHandlerProvider;
import com.ztrue.framework.core.event.IDomainEvent;
import com.ztrue.framework.exception.InvalidOperationException;
import com.ztrue.framework.exception.VersionMismatchException;

import java.util.*;

/**
 * 聚合根抽象
 *
 * @author zhangw
 * @date 2016/10/26 15:31
 * @version: 1.0
 */
public abstract class AggregateRoot<ATID extends Object> implements IAggregateRoot {
    /**
     * 聚合根唯一标识符
     */
    private ATID id;
    private Long version = 0L;
    private Date createDate = new Date();
    private transient IAggregateRootInternalHandlerProvider aggregateRootInternalHandlerProvider;
    /**
     * 未提交事件集合
     */
    private Stack<IDomainEvent> uncommittedEvents;

    public AggregateRoot() {
        uncommittedEvents = new Stack<IDomainEvent>();
        aggregateRootInternalHandlerProvider = ApplicationContextProvider.getAggregateRootInternalHandlerProvider();
    }

    public AggregateRoot(ATID id) {
        this();
        this.id = id;
    }

    /**
     * 应用一个领域事件到当前聚合根
     *
     * @param domainEvent
     */
    protected void applyEvent(IDomainEvent<ATID> domainEvent) {
        if (this.id == null) throw new NullPointerException("聚合根ID不可为空");
        domainEvent.setAggregateRootId(this.id);
        domainEvent.setVersion(this.version + 1);
        handleEvent(domainEvent);
        appendUncommittedEvent(domainEvent);
    }

    /**
     * 应用多个领域事件到当前聚合根
     *
     * @param domainEvents
     */
    protected void applyEvents(IDomainEvent<ATID>... domainEvents) {
        int l = domainEvents.length;
        for (int i = 0; i < l; i++) {
            applyEvent(domainEvents[i]);
        }
    }

    /**
     * 处理一个领域事件
     *
     * @param domainEvent
     */
    private void handleEvent(IDomainEvent<ATID> domainEvent) {
        aggregateRootInternalHandlerProvider.handle(this, domainEvent);
    }

    /**
     * 添加未提交领域事件
     *
     * @param domainEvent
     */
    private void appendUncommittedEvent(IDomainEvent<ATID> domainEvent) {
        if (uncommittedEvents == null) uncommittedEvents = new Stack<IDomainEvent>();
        String domainEventClass = domainEvent.getClass().getName();
        if (uncommittedEvents.stream().filter((e) -> (e.getClass().getName().equals(domainEventClass))).count() > 0) {
            throw new InvalidOperationException(String.format("无法应用重复的领域事件类型：%s，当前聚合根类型：%s，ID：%s", domainEvent.getClass().getName(), this.getClass().getName(), this.getUniqueId()));
        }
        uncommittedEvents.push(domainEvent);
    }

    /**
     * 获取变更事件
     *
     * @return
     */
    public List<IDomainEvent> getChanges() {
        if (uncommittedEvents == null) uncommittedEvents = new Stack<IDomainEvent>();
        return uncommittedEvents;
    }

    /**
     * 接收变更
     *
     * @param newVersion
     */
    public void acceptChanges(Long newVersion) {
        if (this.version + 1 != newVersion) {
            throw new VersionMismatchException(String.format("无法接受无效版本：%d，预期版本：%d，当前聚合根类型：%s，ID：%s", newVersion, this.version + 1, this.getClass().getName(), this.getUniqueId()));
        }
        this.version = newVersion;
        uncommittedEvents.clear();
    }

    /**
     * 事件回溯
     *
     * @param events
     */
    public void replayEvents(Iterator<IDomainEvent> events) {
        while (events.hasNext()) {
            handleEvent(events.next());
        }
    }

    /**
     * 获取当前聚合根ID
     *
     * @return
     */
    public ATID getId() {
        return id;
    }

    /**
     * 设置当前聚合根ID
     *
     * @param id
     */
    public void setId(ATID id) {
        this.id = id;
    }


    /**
     * 获取统一ID
     *
     * @return
     */
    public String getUniqueId() {
        return id == null ? null : id.toString();
    }

    /**
     * 获取当前聚合根版本
     *
     * @return
     */
    public Long getVersion() {
        return this.version;
    }
}